package com.gitee.wsl.mathematics.geometry.d3.ext

import com.gitee.wsl.ext.base.EPS
import com.gitee.wsl.mathematics.coordinate.d3.Coordinate3
import com.gitee.wsl.mathematics.coordinate.d3.Coordinate3D
import com.gitee.wsl.mathematics.geometry.d3.edge.Edge
import com.gitee.wsl.mathematics.coordinate.ext.distanceAtSegment

enum class MidPoint { Tangent, Center, Closest }


// returns 0.0 when tangent point is a, 1.0 -- when it is b, or a fraction in between
fun<T:Number> tangentFraction(a: Coordinate3<T, *>, b: Coordinate3<T, *>): T =
    a.run {
        val dx = b.x - a.x
        val dy = b.y - a.y
        val dz = b.z - a.z
        return -(a.x * dx + a.y * dy + a.z * dz) / (sqr(dx) + sqr(dy) + sqr(dz))
    }


// distance from origin to line A-B
fun<T:Number> tangentDistance(a: Coordinate3<T, *>, b: Coordinate3<T, *>): T =
    tangentFraction(a, b).distanceAtSegment(a, b)

fun isTangentInSegment(a: Coordinate3D<*>, b: Coordinate3D<*>): Boolean =
    tangentFraction(a, b) in Double.EPS..1 - Double.EPS

fun midPointFraction(a: Coordinate3D<*>, b: Coordinate3D<*>, midPoint: MidPoint): Double {
    if (midPoint == MidPoint.Center) return 0.5
    val f = tangentFraction(a, b)
    if (f !in Double.EPS..1 - Double.EPS) { // !isTangentInSegment
        if (midPoint == MidPoint.Tangent) return 0.5
        return if (a.length < b.length + Double.EPS) 0.0 else 1.0 // closest
    }
    return f
}

fun Edge.isTangentInSegment(): Boolean =
    isTangentInSegment(a, b)

fun Edge.midPointFraction(midPoint: MidPoint): Double =
    midPointFraction(a, b, midPoint)

fun Edge.midPoint(midPoint: MidPoint) =
    midPointFraction(midPoint).atSegment(a, b)

fun Edge.tangentPoint() =
    tangentFraction(a, b).atSegment(a, b)

fun Edge.tangentDistance(): Double =
    tangentFraction(a, b).distanceAtSegment(a, b)